trait GenericPrinter[T] {
    public function print(value: T): Void;
}

implement GenericPrinter[Int] for Int {
    public function print(value: Int) {
        printf("%i %i\n", this, value);
    }
}

implement GenericPrinter[Float] for Int {
    public function print(value: Float) {
        printf("%.2f %.2f\n", this as Float, value);
    }
}

implement GenericPrinter[CString] for CString {
    public function print(value: CString) {
        printf("%s %s\n", this, value);
    }
}

function main() {
    var a: Int = 1;
    var aBox: Box[GenericPrinter[Int]] = a;
    aBox.print(2);

    var b: Int = 1;
    var bBox: Box[GenericPrinter[Float]] = b;
    bBox.print(3);

    var c = "hello";
    var cBox: Box[GenericPrinter[CString]] = c;
    cBox.print("world");

    var d = "hi";
    var dBox: Box[GenericPrinter[CString]] = d;
    dBox.print("there");
}
